总线从端(Factory)实现
简介
本页将记录 BusSlaveFactory 工具及其变体之一的实现。您可以在此处获取有关该工具功能的更多信息 here。
规范
类图如下:
BusSlaveFactory 抽象类定义了每个实现应提供的最低要求:
| 名称 | 描述 | 
|---|---|
| busDataWidth | 返回总线的数据宽度 | 
| read(that,address,bitOffset) | 当通过总线读取地址  | 
| write(that,address,bitOffset) | 当通过总线写入地址  | 
| onWrite(address)(doThat) | 当  | 
| onRead(address)(doThat) | 当  | 
| nonStopWrite(that,bitOffset) | 将通过总线写入的  | 
通过使用它们, BusSlaveFactory 还能够提供许多实用工具:
| 名称 | 返回类型 | 描述 | 
|---|---|---|
| readAndWrite(that,address,bitOffset) | 使  | |
| readMultiWord(that,address) | 创建内存映射以从 ‘address’ 地址读取  that信号。 :如果  that的位宽大于一个字(32位),它将在以下地址上扩展寄存器 | |
| writeMultiWord(that,address) | 创建内存映射以通过总线 ‘address’ 地址写入  that信号。 :如果  that的位宽大于一个字(32位),它将在以下地址上扩展寄存器 | |
| createWriteOnly(dataType,address,bitOffset) | T | 在  | 
| createReadWrite(dataType,address,bitOffset) | T | 在  | 
| createAndDriveFlow(dataType,address,bitOffset) | Flow[T] | 在  | 
| drive(that,address,bitOffset) | 使用位于  | |
| driveAndRead(that,address,bitOffset) | 使用位于  | |
| driveFlow(that,address,bitOffset) | 当对  | |
| readStreamNonBlocking(that,address, validBitOffset,payloadBitOffset) | 读取  that信号并在读取address地址时消耗事务。valid <= validBitOffset bit payload <= payloadBitOffset+widthOf(payload) downto  payloadBitOffset | |
| doBitsAccumulationAndClearOnRead (that,address,bitOffset) | 实例化一个内部寄存器,该寄存器在每个周期执行以下操作: reg := reg | that 然后,当发生读取时,寄存器被清除。该寄存器可通过  address地址读取,并放置在字中的bitOffset位置 | 
关于 BusSlaveFactoryDelayed,它仍然是一个抽象类,但它捕获每个原语(BusSlaveFactoryElement)对数据模型的调用。该数据模型是一个包含所有原语的列表,也是一个 HashMap,它将使用的每个地址链接到正在使用它的原语列表。然后,当它们全部被收集时(在当前组件的末尾),它会执行一个回调,该回调应该由扩展它的类实现。该回调函数中实现了这个原语列表中每个原语相对应的硬件。
实现
BusSlaveFactory
让我们来描述一下原语抽象函数:
trait BusSlaveFactory  extends Area {
  def busDataWidth : Int
  def read(that : Data,
           address : BigInt,
           bitOffset : Int = 0) : Unit
  def write(that : Data,
            address : BigInt,
            bitOffset : Int = 0) : Unit
  def onWrite(address : BigInt)(doThat : => Unit) : Unit
  def onRead (address : BigInt)(doThat : => Unit) : Unit
  def nonStopWrite( that : Data,
                    bitOffset : Int = 0) : Unit
  // ...
}
然后让我们利用这些来实现一些有用的工具:
trait BusSlaveFactory  extends Are {
  // ...
  def readAndWrite(that : Data,
                   address: BigInt,
                   bitOffset : Int = 0): Unit = {
    write(that,address,bitOffset)
    read(that,address,bitOffset)
  }
  def drive(that : Data,
            address : BigInt,
            bitOffset : Int = 0) : Unit = {
    val reg = Reg(that)
    write(reg,address,bitOffset)
    that := reg
  }
  def driveAndRead(that : Data,
                   address : BigInt,
                   bitOffset : Int = 0) : Unit = {
    val reg = Reg(that)
    write(reg,address,bitOffset)
    read(reg,address,bitOffset)
    that := reg
  }
  def driveFlow[T <: Data](that : Flow[T],
                           address: BigInt,
                           bitOffset : Int = 0) : Unit = {
    that.valid := False
    onWrite(address) {
      that.valid := True
    }
    nonStopWrite(that.payload,bitOffset)
  }
  def createReadWrite[T <: Data](dataType: T,
                                 address: BigInt,
                                 bitOffset : Int = 0): T = {
    val reg = Reg(dataType)
    write(reg,address,bitOffset)
    read(reg,address,bitOffset)
    reg
  }
  def createAndDriveFlow[T <: Data](dataType : T,
                                 address: BigInt,
                                 bitOffset : Int = 0) : Flow[T] = {
    val flow = Flow(dataType)
    driveFlow(flow,address,bitOffset)
    flow
  }
  def doBitsAccumulationAndClearOnRead(   that : Bits,
                                          address : BigInt,
                                          bitOffset : Int = 0): Unit = {
    assert(that.getWidth <= busDataWidth)
    val reg = Reg(that)
    reg := reg | that
    read(reg,address,bitOffset)
    onRead(address) {
      reg := that
    }
  }
  def readStreamNonBlocking[T <: Data] (that : Stream[T],
                                        address: BigInt,
                                        validBitOffset : Int,
                                        payloadBitOffset : Int) : Unit = {
    that.ready := False
    onRead(address) {
      that.ready := True
    }
    read(that.valid  ,address,validBitOffset)
    read(that.payload,address,payloadBitOffset)
  }
  def readMultiWord(that : Data,
                address : BigInt) : Unit  = {
    val wordCount = (widthOf(that) - 1) / busDataWidth + 1
    val valueBits = that.asBits.resize(wordCount*busDataWidth)
    val words = (0 until wordCount).map(id => valueBits(id * busDataWidth , busDataWidth bits))
    for (wordId <- (0 until wordCount)) {
      read(words(wordId), address + wordId*busDataWidth/8)
    }
  }
  def writeMultiWord(that : Data,
                 address : BigInt) : Unit  = {
    val wordCount = (widthOf(that) - 1) / busDataWidth + 1
    for (wordId <- (0 until wordCount)) {
      write(
        that = new DataWrapper {
          override def getBitsWidth: Int =
            Math.min(busDataWidth, widthOf(that) - wordId * busDataWidth)
          override def assignFromBits(value : Bits): Unit = {
            that.assignFromBits(
              bits     = value.resized,
              offset   = wordId * busDataWidth,
              bitCount = getBitsWidth bits)
          }
        },address = address + wordId * busDataWidth / 8,0
      )
    }
  }
}
BusSlaveFactoryDelayed
让我们实现用于存储原语的类:
trait BusSlaveFactoryElement
// Ask to make `that` readable when a access is done on `address`.
// bitOffset specify where `that` is placed on the answer
case class BusSlaveFactoryRead(that : Data,
                               address : BigInt,
                               bitOffset : Int) extends BusSlaveFactoryElement
// Ask to make `that` writable when a access is done on `address`.
// bitOffset specify where `that` get bits from the request
case class BusSlaveFactoryWrite(that : Data,
                                address : BigInt,
                                bitOffset : Int) extends BusSlaveFactoryElement
// Ask to execute `doThat` when a write access is done on `address`
case class BusSlaveFactoryOnWrite(address : BigInt,
                                  doThat : () => Unit) extends BusSlaveFactoryElement
// Ask to execute `doThat` when a read access is done on `address`
case class BusSlaveFactoryOnRead( address : BigInt,
                                  doThat : () => Unit) extends BusSlaveFactoryElement
// Ask to constantly drive `that` with the data bus
// bitOffset specify where `that` get bits from the request
case class BusSlaveFactoryNonStopWrite(that : Data,
                                       bitOffset : Int) extends BusSlaveFactoryElement
然后让我们实现 BusSlaveFactoryDelayed 本身:
trait BusSlaveFactoryDelayed extends BusSlaveFactory {
  // elements is an array of all BusSlaveFactoryElement requested
  val elements = ArrayBuffer[BusSlaveFactoryElement]()
  // elementsPerAddress is more structured than elements, it group all BusSlaveFactoryElement per requested addresses
  val elementsPerAddress = collection.mutable.HashMap[BigInt,ArrayBuffer[BusSlaveFactoryElement]]()
  private def addAddressableElement(e : BusSlaveFactoryElement,address : BigInt) = {
    elements += e
    elementsPerAddress.getOrElseUpdate(address, ArrayBuffer[BusSlaveFactoryElement]()) += e
  }
  override def read(that : Data,
           address : BigInt,
           bitOffset : Int = 0) : Unit  = {
    assert(bitOffset + that.getBitsWidth <= busDataWidth)
    addAddressableElement(BusSlaveFactoryRead(that,address,bitOffset),address)
  }
  override def write(that : Data,
            address : BigInt,
            bitOffset : Int = 0) : Unit  = {
    assert(bitOffset + that.getBitsWidth <= busDataWidth)
    addAddressableElement(BusSlaveFactoryWrite(that,address,bitOffset),address)
  }
  def onWrite(address : BigInt)(doThat : => Unit) : Unit = {
    addAddressableElement(BusSlaveFactoryOnWrite(address,() => doThat),address)
  }
  def onRead (address : BigInt)(doThat : => Unit) : Unit = {
    addAddressableElement(BusSlaveFactoryOnRead(address,() => doThat),address)
  }
  def nonStopWrite( that : Data,
                    bitOffset : Int = 0) : Unit = {
    assert(bitOffset + that.getBitsWidth <= busDataWidth)
    elements += BusSlaveFactoryNonStopWrite(that,bitOffset)
  }
  // This is the only thing that should be implement by class that extends BusSlaveFactoryDelayed
  def build() : Unit
  component.addPrePopTask(() => build())
}
AvalonMMSlaveFactory
首先,让我们实现提供兼容 AvalonMM 配置对象的伴随对象,对应于下表:
| 信号名称 | 类型 | 描述 | 
|---|---|---|
| read | Bool | 保持一个周期高电平来产生一个读请求 | 
| write | Bool | 保持一个周期高电平来产生一个写请求 | 
| address | UInt(addressWidth bits) | 字节为粒度但是字对齐的 | 
| writeData | Bits(dataWidth bits) | |
| readDataValid | Bool | 保持高电平来响应读命令 | 
| readData | Bits(dataWidth bits) | readDataValid 为高时有效 | 
object AvalonMMSlaveFactory {
  def getAvalonConfig( addressWidth : Int,
                       dataWidth : Int) = {
    AvalonMMConfig.pipelined(   // Create a simple pipelined configuration of the Avalon Bus
      addressWidth = addressWidth,
      dataWidth = dataWidth
    ).copy(                     // Change some parameters of the configuration
      useByteEnable = false,
      useWaitRequestn = false
    )
  }
  def apply(bus : AvalonMM) = new AvalonMMSlaveFactory(bus)
}
然后,让我们实现 AvalonMMSlaveFactory 本身。
class AvalonMMSlaveFactory(bus : AvalonMM) extends BusSlaveFactoryDelayed {
  assert(bus.c == AvalonMMSlaveFactory.getAvalonConfig(bus.c.addressWidth,bus.c.dataWidth))
  val readAtCmd = Flow(Bits(bus.c.dataWidth bits))
  val readAtRsp = readAtCmd.stage()
  bus.readDataValid := readAtRsp.valid
  bus.readData := readAtRsp.payload
  readAtCmd.valid := bus.read
  readAtCmd.payload := 0
  override def build(): Unit = {
    for(element <- elements) element match {
      case element : BusSlaveFactoryNonStopWrite =>
        element.that.assignFromBits(bus.writeData(element.bitOffset, element.that.getBitsWidth bits))
      case _ =>
    }
    for((address,jobs) <- elementsPerAddress) {
      when(bus.address === address) {
        when(bus.write) {
          for(element <- jobs) element match {
            case element : BusSlaveFactoryWrite => {
              element.that.assignFromBits(bus.writeData(element.bitOffset, element.that.getBitsWidth bits))
            }
            case element : BusSlaveFactoryOnWrite => element.doThat()
            case _ =>
          }
        }
        when(bus.read) {
          for(element <- jobs) element match {
            case element : BusSlaveFactoryRead => {
              readAtCmd.payload(element.bitOffset, element.that.getBitsWidth bits) := element.that.asBits
            }
            case element : BusSlaveFactoryOnRead => element.doThat()
            case _ =>
          }
        }
      }
    }
  }
  override def busDataWidth: Int = bus.c.dataWidth
}
结论
就这些了,您可以在此处查看一个使用 Apb3SlaveFactory 来创建 Apb3UartCtrl 的 示例。
如果您想添加对新内存总线的支持,非常简单,您只需继承、实现 BusSlaveFactoryDelayed 特征(trait)的另一个变体即可。 Apb3SlaveFactory 可能是一个很好的参考:D